package com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonCodeEnum;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonResult;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ExceptionCast;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ResponseResult;
import com.xyht.sca_s.student_manage_system.common.util.OrgUserStuUtil;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.entity.SmsWnsChargePaymentCategoryDivide;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.entity.SmsWnsChargePaymentRecord;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.entity.req.PaymentCategoryDivideReq;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.entity.req.SmsWnsChargePaymentRecordReq;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.mapper.SmsWnsChargePaymentCategoryDivideMapper;
import com.xyht.sca_s.student_manage_system.modules.chargePayment.mapper.SmsWnsChargePaymentRecordMapper;
import com.xyht.sca_s.student_manage_system.modules.common.constant.TaskNameExample;
import com.xyht.sca_s.student_manage_system.modules.common.constant.TaskTypeConstant;
import com.xyht.sca_s.student_manage_system.modules.common.entity.SmsImportTask;
import com.xyht.sca_s.student_manage_system.modules.common.entity.SmsImportTaskDetail;
import com.xyht.sca_s.student_manage_system.modules.common.mapper.SmsImportTaskDetailMapper;
import com.xyht.sca_s.student_manage_system.modules.common.mapper.SmsImportTaskMapper;
import com.xyht.sca_s.student_manage_system.modules.newTips.myEnum.RangeTypeEnum;
import com.xyht.sca_s.student_manage_system.modules.newTips.myEnum.TipsTypeEnum;
import com.xyht.sca_s.student_manage_system.modules.newTips.util.NewTipsUtil;
import com.xyht.sca_s.student_manage_system.modules.org.entity.OrgNode;
import com.xyht.sca_s.student_manage_system.modules.org.entity.SmsOrgStructure;
import com.xyht.sca_s.student_manage_system.modules.org.entity.SmsOrgUserRelation;
import com.xyht.sca_s.student_manage_system.modules.org.mapper.SmsOrgStructureMapper;
import com.xyht.sca_s.student_manage_system.modules.org.mapper.SmsOrgUserRelationMapper;
import com.xyht.sca_s.student_manage_system.modules.org.util.NodeUtil;
import com.xyht.sca_s.student_manage_system.modules.signUp.constant.SmsSignUpCommonConstant;
import com.xyht.sca_s.student_manage_system.modules.student.entity.SmsStudentInfo;
import com.xyht.sca_s.student_manage_system.modules.student.entity.SmsUserStudentRelation;
import com.xyht.sca_s.student_manage_system.modules.student.entity.resp.SmsStudentInfoResp;
import com.xyht.sca_s.student_manage_system.modules.student.mapper.SmsStudentInfoMapper;
import com.xyht.sca_s.student_manage_system.modules.student.mapper.SmsUserStudentRelationMapper;
import com.xyht.sca_s.student_manage_system.modules.user.entity.SmsUser;
import com.xyht.sca_s.student_manage_system.modules.user.mapper.SmsUserMapper;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargePlanConstant;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.*;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.req.ChargeItemBatchAddReq;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.req.OrgOrStuInfoReq;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.req.SmsWnsChargePlanStudentStatusExportReq;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.req.SmsWnsIdListReq;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.entity.resp.*;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.mapper.*;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.service.SmsWnsChargePlanStudentStatusService;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.util.AssistantUtil;
import com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.util.CategoryItemUtil;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import static com.xyht.sca_s.student_manage_system.common.util.ParamCheckUtil.isNullOrEmpty;
import static com.xyht.sca_s.student_manage_system.modules.chargePayment.constant.SmsWnsChargePaymentRecordConstant.CHARGE_PAYMENT_RECORD_MODE_OFFLINE;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskDetailReason.*;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.TaskStatus.*;
import static com.xyht.sca_s.student_manage_system.modules.newTips.myEnum.AppTypeEnum.APP_TYPE_ENUM_PRESS_FOR_MONEY;
import static com.xyht.sca_s.student_manage_system.modules.newTips.myEnum.CopyTypeEnum.COPY_TYPE_ENUM_NONE;
import static com.xyht.sca_s.student_manage_system.modules.student.constant.StudentInfoCommonConstant.PER_SHEET_NUM;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargeCategoryConstant.CHARGE_CATEGORY_TYPE_RADIO_NECESSARY;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargeCategoryConstant.CHARGE_CATEGORY_TYPE_RADIO_NOT_NECESSARY;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargePlanConstant.CHARGE_PLAN_TYPE_COMMON;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargePlanConstant.CHARGE_PLAN_TYPE_NEW_STUDENT;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargePlanStudentStatusConstant.CHARGE_PLAN_STUDENT_STATUS_OFFLINE_METHOD_LIST;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.ChargePlanStudentStatusConstant.CHARGE_PLAN_STUDENT_STATUS_OFFLINE_METHOD_UNKNOWN;
import static com.xyht.sca_s.student_manage_system.modules.welcomeNewStudent.constant.RegistrationProcessDetailConstant.*;

/**
 * <p>
 * 收费计划学生缴费状态表 服务实现类
 * </p>
 *
 * @author XYHT
 * @since 2024-02-21
 */
@Service
public class SmsWnsChargePlanStudentStatusServiceImpl extends ServiceImpl<SmsWnsChargePlanStudentStatusMapper, SmsWnsChargePlanStudentStatus> implements SmsWnsChargePlanStudentStatusService {

    @Resource
    private SmsWnsChargePlanStudentStatusMapper smsWnsChargePlanStudentStatusMapper;
    @Resource
    private SmsWnsChargeCategoryDetailMapper smsWnsChargeCategoryDetailMapper;
    @Resource
    private SmsStudentInfoMapper smsStudentInfoMapper;
    @Resource
    private SmsOrgUserRelationMapper smsOrgUserRelationMapper;
    @Resource
    private SmsUserStudentRelationMapper smsUserStudentRelationMapper;
    @Resource
    private AssistantUtil assistantUtil;
    @Resource
    private SmsWnsChargePlanMapper smsWnsChargePlanMapper;
    @Resource
    private SmsOrgStructureMapper smsOrgStructureMapper;
    @Resource
    @Lazy
    private CategoryItemUtil categoryItemUtil;
    @Resource
    private SmsWnsChargeCategoryMapper smsWnsChargeCategoryMapper;
    @Resource
    private SmsImportTaskMapper smsImportTaskMapper;
    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private SmsImportTaskDetailMapper smsImportTaskDetailMapper;
    @Resource
    private NewTipsUtil newTipsUtil;
    @Resource
    private SmsUserMapper smsUserMapper;
    @Resource
    private SmsWnsChargeOrgItemMapper smsWnsChargeOrgItemMapper;
    @Resource
    private SmsWnsChargePaymentRecordMapper smsWnsChargePaymentRecordMapper;
    @Resource
    private SmsWnsRegistrationProcessDetailMapper smsWnsRegistrationProcessDetailMapper;
    @Resource
    private SmsWnsChargePaymentCategoryDivideMapper smsWnsChargePaymentCategoryDivideMapper;
    @Resource
    private OrgUserStuUtil orgUserStuUtil;

    @Override
    public ResponseResult getPaymentRecords(Integer pageNum, Integer pageSize, String planId, String categoryId, String categoryDetailId,
                                            String stuName, String orgId, Boolean isPaid) {
        List<SmsStudentPaymentRecordsResp> studentPaymentRecordsList = new ArrayList<>();
//        List<SmsStudentPaymentRecordsResp> studentPaymentRecordsCollectList = new ArrayList<>();
        Page<Map<String, Object>> page = new Page<>(pageNum, pageSize);
        QueryWrapper<SmsWnsChargePlanStudentStatus> studentStatusLambdaQueryWrapper = new QueryWrapper<>();
        QueryWrapper<SmsWnsChargePlanStudentStatus> studentStatusQueryWrapper = constructionConditions(studentStatusLambdaQueryWrapper,
                planId, categoryId, categoryDetailId, stuName, 1, orgId, isPaid);

        smsWnsChargePlanStudentStatusMapper.selectMapsPage(page, studentStatusQueryWrapper
                .select("user_id as userId")
                .lambda()
                .groupBy(SmsWnsChargePlanStudentStatus::getUserId));
//        queryWrapper.in("user_id", userIds) // 根据用户ID列表筛选
//                .eq("ispaid", true)       // 筛选出已支付的记录
//                .groupBy("user_id")       // 按照用户ID分组
//                .having("COUNT(*) = SUM(CASE WHEN ispaid THEN 1 ELSE 0 END)");
        List<Map<String, Object>> mapList = page.getRecords();
        if (mapList.isEmpty()) {
            return CommonResult.success(studentPaymentRecordsList);
        } else {
            //userId集合
            List<SmsStudentPaymentDetailResp> smsStudentPaymentDetailList = JSONObject.parseArray(JSON.toJSONString(mapList), SmsStudentPaymentDetailResp.class);
            studentPaymentRecordsList = smsStudentPaymentDetailList.stream()
                    .map(smsStudentPaymentDetail -> {
                        QueryWrapper<SmsWnsChargePlanStudentStatus> statusLambdaQueryWrapper = new QueryWrapper<>();
                        QueryWrapper<SmsWnsChargePlanStudentStatus> studentWrapper = constructionConditions(statusLambdaQueryWrapper,
                                planId, categoryId, categoryDetailId, stuName, 2, orgId, isPaid);
                        List<SmsWnsChargePlanStudentStatus> studentStatusList = smsWnsChargePlanStudentStatusMapper.selectList(studentWrapper
                                .lambda()
                                .eq(SmsWnsChargePlanStudentStatus::getUserId, smsStudentPaymentDetail.getUserId()));

                        //学生信息和缴费信息
                        SmsStudentPaymentRecordsResp smsStudentPaymentRecordsResp = new SmsStudentPaymentRecordsResp();
                        BigDecimal totalAmountSum = new BigDecimal(0);
                        BigDecimal needPaymentAmountSum = new BigDecimal(0);
                        BigDecimal paymentAmountSum = new BigDecimal(0);
                        //辅导员
                        List<SmsUser> assistantInfo = assistantUtil.getAssistantByUserId(smsStudentPaymentDetail.getUserId());
                        if (assistantInfo != null && assistantInfo.size() > 0) {
                            smsStudentPaymentRecordsResp.setAssistantInfo(assistantInfo.get(0));
                        }
                        SmsUserStudentRelation smsUserStudentRelation = smsUserStudentRelationMapper.selectOne(new LambdaQueryWrapper<SmsUserStudentRelation>()
                                .eq(SmsUserStudentRelation::getUserId, smsStudentPaymentDetail.getUserId()));
                        if (!isNullOrEmpty(smsUserStudentRelation)) {
                            String stuId = smsUserStudentRelation.getStuId();
                            SmsStudentInfo smsStudentInfo = smsStudentInfoMapper.selectById(stuId);
                            if (!isNullOrEmpty(smsStudentInfo)) {
                                SmsStudentInfoResp smsStudentInfoResp = new SmsStudentInfoResp();
                                BeanUtils.copyProperties(smsStudentInfo, smsStudentInfoResp);
                                smsStudentPaymentRecordsResp.setStudentInfo(smsStudentInfoResp);

                                //缴费信息(应缴,实缴,欠缴)
                                List<SmsStudentPaymentDetailResp> studentPaymentDetailList = new ArrayList<>();
                                for (SmsWnsChargePlanStudentStatus smsWnsChargePlanStudentStatus : studentStatusList) {
                                    //应缴费总计
                                    totalAmountSum = totalAmountSum.add(smsWnsChargePlanStudentStatus.getTotalAmount());
                                    //欠费总计
                                    needPaymentAmountSum = needPaymentAmountSum.add(smsWnsChargePlanStudentStatus.getNeedPaymentAmount());

                                    SmsStudentPaymentDetailResp studentPaymentDetailResp = new SmsStudentPaymentDetailResp();
                                    SmsWnsChargeCategoryDetail smsWnsChargeCategoryDetail = smsWnsChargeCategoryDetailMapper.selectAllById(smsWnsChargePlanStudentStatus.getCategoryDetailId());
                                    if (!isNullOrEmpty(smsWnsChargeCategoryDetail)) {
                                        String categoryDetailName = smsWnsChargeCategoryDetail.getName();
                                        studentPaymentDetailResp.setCategoryDetailName(categoryDetailName);
                                        BeanUtils.copyProperties(smsWnsChargePlanStudentStatus, studentPaymentDetailResp);
                                        //实缴总计
                                        BigDecimal paymentAmount = new BigDecimal(0);
                                        if (!isNullOrEmpty(smsWnsChargePlanStudentStatus.getPaymentAmount())) {
                                            paymentAmount = smsWnsChargePlanStudentStatus.getPaymentAmount();
                                        }
                                        studentPaymentDetailResp.setPaymentAmount(paymentAmount);
                                        paymentAmountSum = paymentAmountSum.add(paymentAmount);
                                    }
                                    studentPaymentDetailList.add(studentPaymentDetailResp);
                                }
                                smsStudentPaymentRecordsResp.setStudentPaymentDetailList(studentPaymentDetailList);
                                smsStudentPaymentRecordsResp.setTotalAmountSum(totalAmountSum);
                                smsStudentPaymentRecordsResp.setPaymentAmountSum(paymentAmountSum);
                                smsStudentPaymentRecordsResp.setNeedPaymentAmountSum(needPaymentAmountSum);
                            }
                        }
                        return smsStudentPaymentRecordsResp;

                    }).collect(Collectors.toList());
//            studentPaymentRecordsCollectList = studentPaymentRecordsList.stream().skip((long) (pageNum - 1) * pageSize)
//                    .limit(pageSize).collect(Collectors.toList());
        }
        return CommonResult.success(studentPaymentRecordsList, (int) page.getTotal());
    }

    @Override
    @Async
    public Future<ResponseResult> exportPaymentRecords(SmsWnsChargePlanStudentStatusExportReq exportReq, HttpServletResponse response) {
        try {
            if (isNullOrEmpty(exportReq.getPlanId())) {
                return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
            }

            SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(exportReq.getPlanId());
            if (plan == null) {
                return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST));
            }

            // 校验收费类别详情是否存在
            List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectList(new LambdaQueryWrapper<SmsWnsChargeCategoryDetail>()
                    .in(SmsWnsChargeCategoryDetail::getId, JSON.parseArray(plan.getDetailIdList(), String.class))
                    .orderByAsc(SmsWnsChargeCategoryDetail::getCategorySort)
                    .orderByAsc(SmsWnsChargeCategoryDetail::getSort));
            if (detailList.isEmpty()) {
                return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_CATEGORY_DETAIL_NOT_EXIST));
            }

            List<SmsStudentPaymentRecordsExportResp> studentPaymentRecordsList = getStudentPaymentRecords(exportReq, 1);
            //声明一个工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            if (studentPaymentRecordsList.size() > 0) {
                double sheetNum = Math.ceil((double) studentPaymentRecordsList.size() / PER_SHEET_NUM);
                for (int k = 0; k < sheetNum; k++) {
                    //生成一个表格，设置表格名称为"缴费记录导出表"
                    HSSFSheet sheet = workbook.createSheet("缴费明细记录导出表" + (k + 1));

                    //设置表格列宽度为12个字节
                    sheet.setDefaultColumnWidth(12);
                    //创建标题的显示样式
                    HSSFCellStyle headerStyle = workbook.createCellStyle();
                    headerStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
                    headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                    //设置单元格字体
                    HSSFFont f = workbook.createFont();
                    f.setFontHeightInPoints((short) 10); //字号
                    f.setBold(true); //加粗
                    headerStyle.setFont(f);
                    //创建第一行表头
                    HSSFRow headRow = sheet.createRow(0);
                    // 固定字段数量
                    int fixedFieldNum = 15;
                    int columnNum = fixedFieldNum + detailList.size();
                    //遍历添加表头
                    for (int i = 0; i < columnNum; i++) {
                        //创建一个单元格
                        String columnName = "";
                        switch (i) {
                            case 0:
                                columnName = "序号";
                                break;
                            case 1:
                                columnName = "注册号";
                                break;
                            case 2:
                                columnName = "状态";
                                break;
                            case 3:
                                columnName = "姓名";
                                break;
                            case 4:
                                columnName = "性别";
                                break;
                            case 5:
                                columnName = "身份证号码";
                                break;
                            case 6:
                                columnName = "学院名称";
                                break;
                            case 7:
                                columnName = "专业";
                                break;
                            case 8:
                                columnName = "年级";
                                break;
                            case 9:
                                columnName = "班级";
                                break;
                            case 10:
                                columnName = "辅导员";
                                break;
                            case 11:
                                columnName = "应缴合计";
                                break;
                            case 12:
                                columnName = "实缴合计";
                                break;
                            case 13:
                                columnName = "欠缴合计";
                                break;
                            case 14:
                                columnName = "应退合计";
                                break;
                            default:
//                                for (int j = 0; j < detailList.size(); j++) {
//                                    i = 14 + j * 3;
//                                    columnName = detailList.get(j).getName() + "应缴金额";
//                                    setTableHead(headRow, headerStyle, i, columnName);
//                                    i = 15 + j * 3;
//                                    columnName = detailList.get(j).getName() + "实缴金额";
//                                    setTableHead(headRow, headerStyle, i, columnName);
//                                    i = 16 + j * 3;
//                                    columnName = detailList.get(j).getName() + "欠缴金额";
//                                    setTableHead(headRow, headerStyle, i, columnName);
//                                }

                                // 金额分类
                                for (int j = 0; j < 4; j++) {
                                    if (j == 0) {
                                        columnName = detailList.get(i - fixedFieldNum).getName() + "应缴金额"; // 14
                                    } else if (j == 1) {
                                        columnName = detailList.get(i - fixedFieldNum).getName() + "实收金额"; // 14 + detailList.size()
                                    } else if (j == 2) {
                                        columnName = detailList.get(i - fixedFieldNum).getName() + "欠缴金额"; // 14 + detailList.size() * 2
                                    } else {
                                        columnName = detailList.get(i - fixedFieldNum).getName() + "应退金额"; // 14 + detailList.size() * 3
                                    }

                                    int index = i + j * detailList.size();
                                    setTableHead(headRow, headerStyle, index, columnName);
                                }
                                break;
                        }

                        if (i < fixedFieldNum) {
                            HSSFCell cell = headRow.createCell(i);
                            cell.setCellStyle(headerStyle);
                            //创建一个内容对象
                            HSSFRichTextString text = new HSSFRichTextString(columnName);
                            //将内容对象的文字内容写入到单元格中
                            cell.setCellValue(text);
                        }
                    }
                    //设置内容
                    int range = PER_SHEET_NUM;
                    //如果是最后一个 sheet 页, 修改 range 值
                    if (k + 1 == sheetNum) {
                        range = studentPaymentRecordsList.size() - (PER_SHEET_NUM) * k;
                    }
                    //设置其他行数据
                    for (int i = 0; i < range; i++) {
                        //设置数据
                        SmsStudentPaymentRecordsExportResp exportResp = studentPaymentRecordsList.get(SmsSignUpCommonConstant.PER_SHEET_NUM * k + i);
                        //创建一行
                        HSSFRow row = sheet.createRow(i + 1);
                        //序号
                        row.createCell(0).setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));

                        // 每一项缴费详情
                        List<SmsStudentPaymentDetailResp> studentPaymentDetailList = exportResp.getStudentPaymentDetailList();
                        //设置该行数据
                        for (int j = 1; j < columnNum; j++) {
                            String data = "";
                            switch (j) {
                                case 1:
                                    data = exportResp.getRegistrationNo();
                                    break;
                                case 2:
                                    data = exportResp.getStuStatus();
                                    break;
                                case 3:
                                    data = exportResp.getStuName();
                                    break;
                                case 4:
                                    data = exportResp.getStuSex();
                                    break;
                                case 5:
                                    data = exportResp.getStuIdCard();
                                    break;
                                case 6:
                                    data = exportResp.getStuCollege();
                                    break;
                                case 7:
                                    data = exportResp.getStuMajor();
                                    break;
                                case 8:
                                    data = exportResp.getStuGrade();
                                    break;
                                case 9:
                                    data = exportResp.getStuClass();
                                    break;
                                case 10:
                                    data = isNullOrEmpty(exportResp.getAssistantInfo()) ? "" : exportResp.getAssistantInfo().getRealName();
                                    break;
                                case 11:
                                    data = String.valueOf(exportResp.getTotalAmountSum());
                                    break;
                                case 12:
                                    data = String.valueOf(exportResp.getPaymentAmountSum());
                                    break;
                                case 13:
                                    data = String.valueOf(exportResp.getNeedPaymentAmountSum());
                                    break;
                                case 14:
                                    data = String.valueOf(exportResp.getNeedPaymentAmountSum().negate());
                                    break;
                                default: //其他
//                                    for (SmsStudentPaymentDetailResp smsStudentPaymentDetailResp : studentPaymentDetailList) {
//                                        data = String.valueOf(smsStudentPaymentDetailResp.getTotalAmount()); // 应缴
//                                        row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
//                                        j = j + 1;
//                                        data = String.valueOf(smsStudentPaymentDetailResp.getPaymentAmount()); // 实缴
//                                        row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
//                                        j = j + 1;
//                                        data = String.valueOf(smsStudentPaymentDetailResp.getNeedPaymentAmount()); // 欠缴
//                                        row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
//                                        j = j + 1;
//                                    }
//                                    continue;

                                    // 金额分类
                                    for (int l = 0; l < 4; l++) {
                                        if (l == 0) { // 应缴
                                            data = String.valueOf(studentPaymentDetailList.get(j - fixedFieldNum).getTotalAmount());
                                        } else if (l == 1) { // 实缴金额
                                            data = String.valueOf(studentPaymentDetailList.get(j - fixedFieldNum).getPaymentAmount());
                                        } else if (l == 2) { // 欠费金额
                                            if (studentPaymentDetailList.get(j - fixedFieldNum).getNeedPaymentAmount().compareTo(BigDecimal.ZERO) < 0) {
                                                data = "0";
                                            } else {
                                                data = String.valueOf(studentPaymentDetailList.get(j - fixedFieldNum).getNeedPaymentAmount());
                                            }
                                        } else { // 应退金额
                                            if (studentPaymentDetailList.get(j - fixedFieldNum).getNeedPaymentAmount().compareTo(BigDecimal.ZERO) < 0) {
                                                data = String.valueOf(studentPaymentDetailList.get(j - fixedFieldNum).getNeedPaymentAmount().negate());
                                            } else {
                                                data = "0";
                                            }
                                        }

                                        int index = j + l * detailList.size();
                                        row.createCell(index).setCellValue(new HSSFRichTextString(data == null ? "" : data));
                                    }
                                    break;
                            }

                            if (j < fixedFieldNum) {
                                row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
                            }
                        }
                    }
                }

            }

            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("缴费明细记录导出表.xlsx", "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode("缴费明细记录导出表.xlsx", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            //刷新缓冲
            response.flushBuffer();
            //workbook将Excel写入到response的输出流中，供页面下载
            OutputStream os = response.getOutputStream();
            workbook.write(os);

            workbook.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }
        return null;
    }

    @Override
    @Async
    public Future<ResponseResult> exportPaymentSumRecords(SmsWnsChargePlanStudentStatusExportReq exportReq, HttpServletResponse response) {
        try {

            List<SmsStudentPaymentRecordsExportResp> studentPaymentRecordsList = getStudentPaymentRecords(exportReq, 2);

            //声明一个工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            if (studentPaymentRecordsList.size() > 0) {
                double sheetNum = Math.ceil((double) studentPaymentRecordsList.size() / PER_SHEET_NUM);
                for (int k = 0; k < sheetNum; k++) {
                    //生成一个表格，设置表格名称为"缴费记录导出表"
                    HSSFSheet sheet = workbook.createSheet("缴费总记录导出表" + (k + 1));

                    //设置表格列宽度为12个字节
                    sheet.setDefaultColumnWidth(12);
                    //创建标题的显示样式
                    HSSFCellStyle headerStyle = workbook.createCellStyle();
                    headerStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
                    headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                    //设置单元格字体
                    HSSFFont f = workbook.createFont();
                    f.setFontHeightInPoints((short) 10); //字号
                    f.setBold(true); //加粗
                    headerStyle.setFont(f);
                    //创建第一行表头
                    HSSFRow headRow = sheet.createRow(0);
                    int columnNum = 14;
                    //遍历添加表头
                    for (int i = 0; i < columnNum; i++) {
                        //创建一个单元格
                        String columnName;
                        switch (i) {
                            case 0:
                                columnName = "序号";
                                break;
                            case 1:
                                columnName = "注册号";
                                break;
                            case 2:
                                columnName = "状态";
                                break;
                            case 3:
                                columnName = "姓名";
                                break;
                            case 4:
                                columnName = "性别";
                                break;
                            case 5:
                                columnName = "身份证号码";
                                break;
                            case 6:
                                columnName = "学院名称";
                                break;
                            case 7:
                                columnName = "专业";
                                break;
                            case 8:
                                columnName = "年级";
                                break;
                            case 9:
                                columnName = "班级";
                                break;
                            case 10:
                                columnName = "辅导员";
                                break;
                            case 11:
                                columnName = "应缴合计";
                                break;
                            case 12:
                                columnName = "实缴合计";
                                break;
                            default:
                                columnName = "欠缴合计";
                                break;
                        }
                        HSSFCell cell = headRow.createCell(i);
                        cell.setCellStyle(headerStyle);
                        //创建一个内容对象
                        HSSFRichTextString text = new HSSFRichTextString(columnName);
                        //将内容对象的文字内容写入到单元格中
                        cell.setCellValue(text);
                    }
                    //设置内容
                    int range = PER_SHEET_NUM;
                    //如果是最后一个 sheet 页, 修改 range 值
                    if (k + 1 == sheetNum) {
                        range = studentPaymentRecordsList.size() - (PER_SHEET_NUM) * k;
                    }
                    //设置其他行数据
                    for (int i = 0; i < range; i++) {
                        //设置数据
                        SmsStudentPaymentRecordsExportResp exportResp = studentPaymentRecordsList.get(SmsSignUpCommonConstant.PER_SHEET_NUM * k + i);
                        //创建一行
                        HSSFRow row = sheet.createRow(i + 1);
                        //序号
                        row.createCell(0).setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));
                        //设置该行数据
                        for (int j = 1; j < columnNum; j++) {
                            String data;
                            switch (j) {
                                case 1:
                                    data = exportResp.getRegistrationNo();
                                    break;
                                case 2:
                                    data = exportResp.getStuStatus();
                                    break;
                                case 3:
                                    data = exportResp.getStuName();
                                    break;
                                case 4:
                                    data = exportResp.getStuSex();
                                    break;
                                case 5:
                                    data = exportResp.getStuIdCard();
                                    break;
                                case 6:
                                    data = exportResp.getStuCollege();
                                    break;
                                case 7:
                                    data = exportResp.getStuMajor();
                                    break;
                                case 8:
                                    data = exportResp.getStuGrade();
                                    break;
                                case 9:
                                    data = exportResp.getStuClass();
                                    break;
                                case 10:
                                    data = isNullOrEmpty(exportResp.getAssistantInfo()) ? "" : exportResp.getAssistantInfo().getRealName();
                                    break;
                                case 11:
                                    data = String.valueOf(exportResp.getTotalAmountSum());
                                    break;
                                case 12:
                                    data = String.valueOf(exportResp.getPaymentAmountSum());
                                    break;
                                default:
                                    data = String.valueOf(exportResp.getNeedPaymentAmountSum());
                                    break;
                            }
                            row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
                        }
                    }
                }

            }

            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("缴费总记录导出表.xlsx", "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode("缴费总记录导出表.xlsx", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            //刷新缓冲
            response.flushBuffer();
            //workbook将Excel写入到response的输出流中，供页面下载
            OutputStream os = response.getOutputStream();
            workbook.write(os);

            workbook.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }
        return null;
    }

    @Override
    public ResponseResult listStuStatus(Integer pageNum, Integer pageSize, String planId, Integer orgId, String searchStr) {
        SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(planId);
        if (plan == null) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST);
        }
        // 校验计划是否初始化
        if (!plan.getIsInitialized()) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_DETAIL_LIST_NOT_EXIST);
        }

        // 查询本计划范围内学生
        List<String> stuInPlanIdList = this.list(new QueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .select("DISTINCT(stu_id)")
                        .lambda().eq(SmsWnsChargePlanStudentStatus::getPlanId, planId))
                .stream().map(SmsWnsChargePlanStudentStatus::getStuId).collect(Collectors.toList());
        LambdaQueryWrapper<SmsStudentInfo> queryWrapper = new LambdaQueryWrapper<SmsStudentInfo>()
                .in(SmsStudentInfo::getId, stuInPlanIdList.isEmpty() ? Collections.singletonList("") : stuInPlanIdList)
                .orderByAsc(SmsStudentInfo::getStuClass);

        if (!isNullOrEmpty(searchStr)) {
            List<SmsStudentInfo> studentInfoList = smsStudentInfoMapper.selectList(new LambdaQueryWrapper<SmsStudentInfo>()
                    .and(i -> i.like(SmsStudentInfo::getStuNo, searchStr).or()
                            .like(SmsStudentInfo::getStuName, searchStr).or()
                            .like(SmsStudentInfo::getStuTel, searchStr).or()
                            .like(SmsStudentInfo::getStuIdCard, searchStr).or()
                            .like(SmsStudentInfo::getStuSex, searchStr).or()
                            .like(SmsStudentInfo::getStuCollege, searchStr).or()
                            .like(SmsStudentInfo::getStuMajor, searchStr).or()
                            .like(SmsStudentInfo::getStuGrade, searchStr).or()
                            .like(SmsStudentInfo::getStuClass, searchStr).or()
                            .like(SmsStudentInfo::getStuNation, searchStr).or()
                            .like(SmsStudentInfo::getStuNativePlace, searchStr)));
            if (studentInfoList.isEmpty()) {
                queryWrapper.in(SmsStudentInfo::getId, "");
            } else {
                List<String> stuIdList = studentInfoList.stream().map(SmsStudentInfo::getId).collect(Collectors.toList());
                queryWrapper.in(SmsStudentInfo::getId, stuIdList);
            }
        }

        if (orgId != null) {
            List<String> orgIdList = JSON.parseArray(plan.getIdList(), String.class);
            if (!orgIdList.contains(String.valueOf(orgId))) {
                return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_RANGE_OUT_OF_TYPE);
            }

            // 专业粒度先缩小查询范围
            if (plan.getRangeType() == ChargePlanConstant.CHARGE_PLAN_RANGE_TYPE_MAJOR) {
                // 获取年级
                List<SmsOrgStructure> gradeOrgList = smsOrgStructureMapper.selectList(new LambdaQueryWrapper<SmsOrgStructure>()
                        .eq(SmsOrgStructure::getParentId, orgId)
                        .eq(SmsOrgStructure::getOrgName, plan.getGrade()));
                if (!gradeOrgList.isEmpty()) {
                    // 将年级赋给orgId
                    orgId = gradeOrgList.get(0).getId();
                }
            }

            // 获取当前年级下所有userId
            List<String> stuIdList = orgUserStuUtil.getStuIdListByOrgId(orgId);
            queryWrapper.in(SmsStudentInfo::getId, stuIdList.isEmpty() ? Collections.singletonList("") : stuIdList);
        }

        Page<SmsStudentInfo> page = new Page<>(pageNum, pageSize);
        smsStudentInfoMapper.selectPage(page, queryWrapper);

        List<String> detailIdList = JSON.parseArray(plan.getDetailIdList(), String.class);
        // 设置收费项集合
        ListStuStatusResp listStuStatusResp = new ListStuStatusResp();
        List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectBatchIds(detailIdList);
        List<SmsWnsChargeCategoryDetailResp> detailRespList = detailList.stream().map(detail -> {
            SmsWnsChargeCategoryDetailResp detailResp = new SmsWnsChargeCategoryDetailResp();
            BeanUtils.copyProperties(detail, detailResp);
            return detailResp;
        }).collect(Collectors.toList());
        listStuStatusResp.setDetailRespList(detailRespList);

        // 设置学生信息和各收费项状态集合
        List<StuInfoAndStatusResp> stuInfoAndStatusRespList = page.getRecords().stream().map(stuInfo -> {
            StuInfoAndStatusResp stuInfoAndStatusResp = new StuInfoAndStatusResp();
            // 设置学生信息
            stuInfoAndStatusResp.setStudentInfo(stuInfo);

            // 设置各项收费情况
            List<SmsWnsChargePlanStudentStatusResp> studentStatusRespList = detailIdList.stream().map(detailId -> {
                SmsWnsChargePlanStudentStatus oneStatus = this.getOne(new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .eq(SmsWnsChargePlanStudentStatus::getPlanId, planId)
                        .eq(SmsWnsChargePlanStudentStatus::getStuId, stuInfo.getId())
                        .eq(SmsWnsChargePlanStudentStatus::getCategoryDetailId, detailId));
                SmsWnsChargePlanStudentStatusResp studentStatusResp = new SmsWnsChargePlanStudentStatusResp();
                BeanUtils.copyProperties(oneStatus, studentStatusResp);
                return studentStatusResp;
            }).collect(Collectors.toList());
            stuInfoAndStatusResp.setStudentStatusRespList(studentStatusRespList);

            return stuInfoAndStatusResp;
        }).collect(Collectors.toList());
        listStuStatusResp.setStuInfoAndStatusRespList(stuInfoAndStatusRespList);

        return CommonResult.success(listStuStatusResp, (int) page.getTotal());
    }

    @Override
    public ResponseResult updNeedPayById(SmsWnsChargePlanStudentStatusResp req) {
        BigDecimal updTotalAmount = req.getTotalAmount();
        if (isNullOrEmpty(req.getId(), updTotalAmount)) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        if (updTotalAmount.compareTo(BigDecimal.ZERO) < 0) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        SmsWnsChargePlanStudentStatus studentStatus = this.getById(req.getId());
        // 修改值不等于原先值则进行操作
        if (studentStatus.getTotalAmount().compareTo(updTotalAmount) != 0) {
            // 设置应缴金额
            studentStatus.setTotalAmount(updTotalAmount);

            // 设置欠费金额(欠费金额 = 应缴 - 实缴)
            BigDecimal paymentAmount = studentStatus.getPaymentAmount() == null ? BigDecimal.ZERO : studentStatus.getPaymentAmount();
            BigDecimal updNeedPaymentAmount = updTotalAmount.subtract(paymentAmount);
            studentStatus.setNeedPaymentAmount(updNeedPaymentAmount);

            // 设置是否缴齐
            // 欠费金额 <= 0 则缴齐
            studentStatus.setIsPaid(updNeedPaymentAmount.compareTo(BigDecimal.ZERO) <= 0);
        }
        this.updateById(studentStatus);

        return CommonResult.success();
    }

    @Override
    public ResponseResult list(String userId) {
        SmsWnsChargePlan plan = categoryItemUtil.getNewStuPlanByUserId(userId);
        if (plan == null) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST);
        }

        List<SmsWnsChargePlanStudentStatus> studentStatusList = this.list(new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                .eq(SmsWnsChargePlanStudentStatus::getPlanId, plan.getId())
                .eq(SmsWnsChargePlanStudentStatus::getUserId, userId));
        if (studentStatusList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_MISSING);
        }

        // 获取所有收费项
        List<String> detailIdList = JSON.parseArray(plan.getDetailIdList(), String.class);
        List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectBatchIds(detailIdList);

        // 构造返回参数
        List<String> categoryIdList = detailList.stream().map(SmsWnsChargeCategoryDetail::getCategoryId).distinct().collect(Collectors.toList());
        List<SmsWnsChargeCategory> categoryList = smsWnsChargeCategoryMapper.selectList(new LambdaQueryWrapper<SmsWnsChargeCategory>()
                .in(SmsWnsChargeCategory::getId, categoryIdList)
                .orderByAsc(SmsWnsChargeCategory::getSort));
        List<SmsWnsChargeCategoryResp> collect = categoryList.stream().map(category -> {
            SmsWnsChargeCategoryResp categoryResp = new SmsWnsChargeCategoryResp();
            BeanUtils.copyProperties(category, categoryResp);

            List<SmsWnsChargeCategoryDetailResp> detailRespList = detailList.stream().filter(detail -> detail.getCategoryId().equals(category.getId()))
                    .map(detail -> {
                        SmsWnsChargeCategoryDetailResp detailResp = new SmsWnsChargeCategoryDetailResp();
                        BeanUtils.copyProperties(detail, detailResp);

                        List<SmsWnsChargePlanStudentStatus> filterStatusList = studentStatusList.stream().filter(status -> status.getCategoryDetailId().equals(detail.getId())).collect(Collectors.toList());
                        SmsWnsChargePlanStudentStatus status = filterStatusList.get(0);
                        SmsWnsChargePlanStudentStatusResp statusResp = new SmsWnsChargePlanStudentStatusResp();
                        BeanUtils.copyProperties(status, statusResp);

                        // 设置总金额以及优惠金额
                        SmsWnsChargeOrgItem chargeOrgItem = smsWnsChargeOrgItemMapper.selectById(status.getChargeItemId());
                        if (chargeOrgItem != null) {
                            statusResp.setChargeItem(chargeOrgItem);
                        }
                        detailResp.setStatusResp(statusResp);

                        return detailResp;
                    }).sorted(Comparator.comparingInt(SmsWnsChargeCategoryDetailResp::getSort)) // 根据sort排序
                    .collect(Collectors.toList());
            categoryResp.setDetailRespList(detailRespList);
            return categoryResp;
        }).collect(Collectors.toList());

        return CommonResult.success(collect);
    }

    @Override
    @Transactional
    public ResponseResult chooseStatus(SmsWnsIdListReq req) {
        if (req.getIdList().isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        List<SmsWnsChargePlanStudentStatus> statusList = this.listByIds(req.getIdList());
        if (statusList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_MISSING);
        }

        List<String> userIdList = statusList.stream().map(SmsWnsChargePlanStudentStatus::getUserId).distinct().collect(Collectors.toList());
        if (userIdList.size() != 1) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_NOT_BELONG_SAME_PERSON);
        }
        String userId = userIdList.get(0);

        List<String> cateGoryIdList = statusList.stream().map(SmsWnsChargePlanStudentStatus::getCategoryId).distinct().collect(Collectors.toList());
        List<SmsWnsChargeCategory> categoryList = smsWnsChargeCategoryMapper.selectBatchIds(cateGoryIdList);
        for (SmsWnsChargeCategory category : categoryList) {
            List<SmsWnsChargePlanStudentStatus> filterStatusList = statusList.stream().filter(status -> status.getCategoryId().equals(category.getId())).collect(Collectors.toList());
            switch (category.getType()) {
                case CHARGE_CATEGORY_TYPE_RADIO_NECESSARY: // 单选必缴
                case CHARGE_CATEGORY_TYPE_RADIO_NOT_NECESSARY: // 单选不必缴
                    if (filterStatusList.size() > 1) {
                        return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_CATEGORY_TYPE_MISMATCHING);
                    }
                    break;
            }
        }

        // 修改其他项为未选择
        SmsWnsChargePlanStudentStatus status = new SmsWnsChargePlanStudentStatus();
        status.setIsNecessary(false); // 未选择此项
        smsWnsChargePlanStudentStatusMapper.update(status, new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                .eq(SmsWnsChargePlanStudentStatus::getUserId, userId)
                .eq(SmsWnsChargePlanStudentStatus::getPlanId, statusList.get(0).getPlanId())
                .notIn(SmsWnsChargePlanStudentStatus::getId, req.getIdList()));

        // 将选中的项改为已选择
        SmsWnsChargePlanStudentStatus updStatus = new SmsWnsChargePlanStudentStatus();
        updStatus.setIsNecessary(true); // 选择此项
        smsWnsChargePlanStudentStatusMapper.update(updStatus, new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                .in(SmsWnsChargePlanStudentStatus::getId, req.getIdList()));

        return CommonResult.success();
    }

    @Override
    public ResponseResult paymentList(Integer type, String userId) {
        if (type != CHARGE_PLAN_TYPE_NEW_STUDENT && type != CHARGE_PLAN_TYPE_COMMON) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        LambdaQueryWrapper<SmsWnsChargePlanStudentStatus> queryWrapper = new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                .eq(SmsWnsChargePlanStudentStatus::getUserId, userId)
                .eq(SmsWnsChargePlanStudentStatus::getIsNecessary, true)
                .orderByDesc(SmsWnsChargePlanStudentStatus::getCreateTime);

        List<SmsWnsChargePlanStudentStatus> studentStatusList = this.list(queryWrapper);
        SmsWnsChargePlan newStuPlan;
        // 新生缴费过滤结果
        if (type == CHARGE_PLAN_TYPE_NEW_STUDENT) {
            if (!studentStatusList.isEmpty()) {
                List<String> planIdList = studentStatusList.stream().map(SmsWnsChargePlanStudentStatus::getPlanId).distinct().collect(Collectors.toList());
                List<SmsWnsChargePlan> planList = smsWnsChargePlanMapper.selectBatchIds(planIdList);
                List<SmsWnsChargePlan> newStuPlanList = planList.stream().filter(plan -> plan.getType() == CHARGE_PLAN_TYPE_NEW_STUDENT).collect(Collectors.toList());
                newStuPlan = newStuPlanList.get(0);
                // 过滤结果
                studentStatusList = studentStatusList.stream().filter(status -> status.getPlanId().equals(newStuPlan.getId())).collect(Collectors.toList());
            } else {
                newStuPlan = null;
            }
        } else {
            newStuPlan = null;
        }

        List<SmsWnsChargePlanStudentStatusResp> statusRespList = studentStatusList.stream().map(status -> {
            SmsWnsChargePlanStudentStatusResp statusResp = new SmsWnsChargePlanStudentStatusResp();
            BeanUtils.copyProperties(status, statusResp);

            if (newStuPlan == null) {
                SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(status.getPlanId());
                if (plan != null) {
                    statusResp.setPlanName(plan.getName());
                }
            } else {
                statusResp.setPlanName(newStuPlan.getName());
            }

            SmsWnsChargeCategory category = smsWnsChargeCategoryMapper.selectAllById(status.getCategoryId());
            if (category != null) {
                statusResp.setCategory(category);
            }

            SmsWnsChargeCategoryDetail detail = smsWnsChargeCategoryDetailMapper.selectAllById(status.getCategoryDetailId());
            if (detail != null) {
                statusResp.setCategoryDetailName(detail.getName());
            }

            return statusResp;
        }).collect(Collectors.toList());

        return CommonResult.success(statusRespList);
    }

    @Override
    @Async
    public Future<ResponseResult> stuStatusExport(SmsWnsChargePlanStudentStatusExportReq exportReq, HttpServletResponse response) {
        if (isNullOrEmpty(exportReq.getPlanId())) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
        }

        // 校验计划是否存在
        SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(exportReq.getPlanId());
        if (plan == null) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST));
        }

        // 校验计划是否初始化(指定收费项)
        if (!plan.getIsInitialized()) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_DETAIL_LIST_NOT_EXIST));
        }

        // 校验收费类别详情是否存在
        List<String> detailIdList = JSON.parseArray(plan.getDetailIdList(), String.class);
        List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectList(new LambdaQueryWrapper<SmsWnsChargeCategoryDetail>()
                .in(SmsWnsChargeCategoryDetail::getId, detailIdList)
                .orderByAsc(SmsWnsChargeCategoryDetail::getCategorySort)
                .orderByAsc(SmsWnsChargeCategoryDetail::getSort));
        if (detailList.isEmpty()) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_CATEGORY_DETAIL_NOT_EXIST));
        }

        // 收集去重本计划范围内学生id
        List<String> stuInPlanIdList = this.list(new QueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .select("DISTINCT(stu_id)")
                        .lambda().eq(SmsWnsChargePlanStudentStatus::getPlanId, plan.getId()))
                .stream().map(SmsWnsChargePlanStudentStatus::getStuId).collect(Collectors.toList());
        // 查询学生信息
        List<SmsStudentInfo> studentInfoList = smsStudentInfoMapper.selectBatchIds(stuInPlanIdList);

        try {
            // 声明一个工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            // 获取sheet页数
            double sheetNum = Math.ceil((double) studentInfoList.size() / PER_SHEET_NUM);
            for (int k = 0; k < sheetNum; k++) {
                // 生成一个 sheet页,设置 sheet页 名称为 "学生应缴项导出表"
                HSSFSheet sheet = workbook.createSheet("学生应缴项导出表" + (k + 1));
                // 设置表格列宽度为 12
                sheet.setDefaultColumnWidth(12);

                // 设置单元格的显示样式
                HSSFCellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                // 设置单元格字体
                HSSFFont font = workbook.createFont();
                font.setFontHeightInPoints((short) 10); // 字号
                font.setBold(true); // 加粗
                cellStyle.setFont(font);

                // 创建第一行表头
                HSSFRow headRow = sheet.createRow(0);
                // 设置第一行数据
                int fixColumnNum = 6;
                int columnNum = fixColumnNum + detailList.size(); // 列数
                for (int i = 0; i < columnNum; i++) {
                    // 创建一个单元格
                    HSSFCell cell = headRow.createCell(i);
                    cell.setCellStyle(cellStyle);

                    String columnName = "";
                    switch (i) {
                        case 0:
                            columnName = "序号";
                            break;
                        case 1:
                            columnName = "年级名";
                            break;
                        case 2:
                            columnName = "专业名";
                            break;
                        case 3:
                            columnName = "班级名";
                            break;
                        case 4:
                            columnName = "学生姓名";
                            break;
                        case 5:
                            columnName = "学号";
                            break;
                        default:
                            columnName = detailList.get(i - 6).getName();
                    }

                    // 创建一个内容对象
                    HSSFRichTextString text = new HSSFRichTextString(columnName);
                    // 将内容对象的文字内容写入到单元格中
                    cell.setCellValue(text);
                }

                // 设置内容
                int range = SmsSignUpCommonConstant.PER_SHEET_NUM;
                // 如果是最后一个 sheet 页, 修改 range 值
                if (k + 1 == sheetNum) {
                    range = studentInfoList.size() - SmsSignUpCommonConstant.PER_SHEET_NUM * k;
                }
                // 设置其他行数据
                for (int i = 0; i < range; i++) {
                    // 获取学生数据
                    SmsStudentInfo studentInfo = studentInfoList.get(SmsSignUpCommonConstant.PER_SHEET_NUM * k + i);

                    // 获取学生应缴费数据
                    List<SmsWnsChargePlanStudentStatus> studentStatusList = smsWnsChargePlanStudentStatusMapper.selectList(new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                            .eq(SmsWnsChargePlanStudentStatus::getPlanId, plan.getId())
                            .eq(SmsWnsChargePlanStudentStatus::getStuId, studentInfo.getId())
                            .in(SmsWnsChargePlanStudentStatus::getCategoryDetailId, detailIdList));
                    List<SmsWnsChargePlanStudentStatus> statusRespList = new ArrayList<>(); // 学生应缴费数据
                    for (SmsWnsChargeCategoryDetail detail : detailList) {
                        List<SmsWnsChargePlanStudentStatus> filterStatusList = studentStatusList.stream().filter(status -> status.getCategoryDetailId().equals(detail.getId())).collect(Collectors.toList());
                        statusRespList.add(filterStatusList.get(0));
                    }

                    // 设置数据
                    // 创建一行
                    HSSFRow row = sheet.createRow(i + 1);
                    // 序号
                    row.createCell(0).setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));
                    // 设置除序号外其他数据
                    for (int j = 1; j < columnNum; j++) {
                        String data;
                        switch (j) {
                            case 1: // 年级名
                                data = studentInfo.getStuGrade();
                                break;
                            case 2: // 专业名
                                data = studentInfo.getStuMajor();
                                break;
                            case 3: // 班级名
                                data = studentInfo.getStuClass();
                                break;
                            case 4: // 学生姓名
                                data = studentInfo.getStuName();
                                break;
                            case 5: // 学号
                                data = studentInfo.getStuNo();
                                break;
                            default: // 其他
                                data = statusRespList.get(j - fixColumnNum).getTotalAmount().toString();
                        }
                        row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
                    }
                }
            }

            // 设置 response
            // response.reset();
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("学生应缴项导出表.xlsx", "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode("学生应缴项导出表.xlsx", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            // 刷新缓冲
            response.flushBuffer();
            // workbook将Excel写入到response的输出流中，供页面下载
            OutputStream os = response.getOutputStream();
            workbook.write(os);
            workbook.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }

        return new AsyncResult<>(CommonResult.success());
    }

    @Override
    @Async
    public Future<ResponseResult> batchUpdStuStatus(ChargeItemBatchAddReq batchAddReq, String userId) {
        if (isNullOrEmpty(batchAddReq.getPlanId(), batchAddReq.getOrgOrStuInfoReqList())) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
        }
        // 校验计划是否存在
        SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(batchAddReq.getPlanId());
        if (plan == null) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST));
        }

        // 校验计划是否指定收费项
        if (!plan.getIsInitialized()) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_DETAIL_LIST_NOT_EXIST));
        }

        // 校验收费类别详情是否存在
        List<String> detailIdList = JSON.parseArray(plan.getDetailIdList(), String.class);
        List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectList(new LambdaQueryWrapper<SmsWnsChargeCategoryDetail>()
                .in(SmsWnsChargeCategoryDetail::getId, detailIdList)
                .orderByAsc(SmsWnsChargeCategoryDetail::getCategorySort)
                .orderByAsc(SmsWnsChargeCategoryDetail::getSort));
        if (detailList.isEmpty()) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.WNS_CHARGE_CATEGORY_DETAIL_NOT_EXIST));
        }

        // 创建导入任务
        SmsImportTask smsImportTask = new SmsImportTask();
        smsImportTask.setTaskName(TaskNameExample.TASK_NAME_CHARGE_ITEM_IMPORT);
        smsImportTask.setUserId(userId);
        smsImportTask.setStatus(TASK_STATUS_DOING); // 任务状态 0:进行中
        smsImportTask.setTaskType(TaskTypeConstant.TASK_TYPE_STUDENT_STATUS_UPDATE);
        smsImportTaskMapper.insert(smsImportTask);
        String taskId = smsImportTask.getId();
        if (isNullOrEmpty(taskId)) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
        }

        // 批量导入新生信息
        int OFFSET_ROW = 1; // 初始化行数
        int taskStatus = TASK_STATUS_DONE; // 任务状态: 完成

        // 获取需要导入的数据
        List<OrgOrStuInfoReq> orgOrStuInfoReqList = batchAddReq.getOrgOrStuInfoReqList();
        for (int i = 0; i < orgOrStuInfoReqList.size(); i++) {
            // 开启事务
            DefaultTransactionDefinition dt = new DefaultTransactionDefinition();
            // 嵌套事务 PROPAGATION_REQUIRES_NEW 每次开启一个新的事务
            dt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 设置嵌套事务
            TransactionStatus status = transactionManager.getTransaction(dt);

            try {
                // 获取组织和学生信息
                OrgOrStuInfoReq orgOrStuInfoReq = orgOrStuInfoReqList.get(i);
                // 获取该学生应缴金额集合
                List<BigDecimal> amountList = orgOrStuInfoReq.getAmountList();

                // 必填字段验证
                if (isNullOrEmpty(orgOrStuInfoReq.getStuNo())) {
                    transactionManager.rollback(status);
                    createImportTaskDetail("", orgOrStuInfoReq.getStuName(), taskId, REASON_STUDENT_NO_NOT_EXIST, String.valueOf(i + OFFSET_ROW));
                    taskStatus = TASK_STATUS_ERR;
                    continue;
                }

                if (isNullOrEmpty(orgOrStuInfoReq.getAmountList()) || orgOrStuInfoReq.getAmountList().size() != detailList.size()) {
                    transactionManager.rollback(status);
                    createImportTaskDetail("", orgOrStuInfoReq.getStuName(), taskId, REASON_AMOUNT_LIST_NOT_EXIST, String.valueOf(i + OFFSET_ROW));
                    taskStatus = TASK_STATUS_ERR;
                    continue;
                }

                // 校验该学生是否存在
                SmsStudentInfo studentInfo = smsStudentInfoMapper.selectOne(new LambdaQueryWrapper<SmsStudentInfo>()
                        .eq(SmsStudentInfo::getStuNo, orgOrStuInfoReq.getStuNo()));
                if (studentInfo == null) {
                    transactionManager.rollback(status);
                    createImportTaskDetail("", orgOrStuInfoReq.getStuName(), taskId, REASON_CHARGE_STUDENT_INFO_NOT_EXIST, String.valueOf(i + OFFSET_ROW));
                    taskStatus = TASK_STATUS_ERR;
                    continue;
                }

                // 获取计划中收费项应缴金额
                List<SmsWnsChargePlanStudentStatus> studentStatusList = this.list(new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .eq(SmsWnsChargePlanStudentStatus::getStuId, studentInfo.getId())
                        .eq(SmsWnsChargePlanStudentStatus::getPlanId, batchAddReq.getPlanId())
                        .in(SmsWnsChargePlanStudentStatus::getCategoryDetailId, detailIdList));
                if (studentStatusList.isEmpty()) {
                    transactionManager.rollback(status);
                    createImportTaskDetail(studentInfo.getId(), orgOrStuInfoReq.getStuName(), taskId, REASON_STUDENT_STATUS_LIST_NOT_EXIST, String.valueOf(i + OFFSET_ROW));
                    taskStatus = TASK_STATUS_ERR;
                    continue;
                }

                // 学生应缴费数据进行排序
                List<SmsWnsChargePlanStudentStatus> statusUpdList = new ArrayList<>();
                for (SmsWnsChargeCategoryDetail detail : detailList) {
                    List<SmsWnsChargePlanStudentStatus> filterStatusList = studentStatusList.stream().filter(stuStatus -> stuStatus.getCategoryDetailId().equals(detail.getId())).collect(Collectors.toList());
                    statusUpdList.add(filterStatusList.get(0));
                }

                // 修改学生应缴费数据
                for (int j = 0; j < statusUpdList.size(); j++) {
                    SmsWnsChargePlanStudentStatus studentStatus = statusUpdList.get(j);

                    // TODO 设置总金额
                    // 接收到的应缴数据
                    BigDecimal totalAmount = amountList.get(j);
                    // 计算欠缴金额(new欠缴金额 = old欠缴金额 + (new应缴金额 - old应缴金额))
                    BigDecimal updAmount = totalAmount.subtract(studentStatus.getTotalAmount());
                    BigDecimal needPaymentAmount = studentStatus.getNeedPaymentAmount().add(updAmount);

                    // 设置应缴
                    studentStatus.setTotalAmount(amountList.get(j));
                    // 设置欠费
                    studentStatus.setNeedPaymentAmount(needPaymentAmount);

                    this.updateById(studentStatus);
                }

                // 提交事务
                transactionManager.commit(status);
            } catch (Exception e) {
                e.printStackTrace();
                // 回滚事务
                transactionManager.rollback(status);
            } finally {
                if (status.isNewTransaction() && !status.isCompleted()) {
                    transactionManager.commit(status);
                }
            }
        }

        // 更新任务状态未已结束
        smsImportTask.setStatus(taskStatus);
        smsImportTaskMapper.updateById(smsImportTask);

        return new AsyncResult<>(CommonResult.success());
    }

    @Override
    public ResponseResult pressForMoney(String userId) {
        // 收集去重学生id
        List<String> userIdList = this.list(new QueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .select("DISTINCT(user_id)")
                        .lambda()
                        .eq(SmsWnsChargePlanStudentStatus::getIsNecessary, true) // 选择此项(必缴)
                        .eq(SmsWnsChargePlanStudentStatus::getIsPaid, false)) // 未缴齐
                .stream().map(SmsWnsChargePlanStudentStatus::getUserId).collect(Collectors.toList());

        for (String noticeUserId : userIdList) {
            SmsUser user = smsUserMapper.selectById(noticeUserId);
            if (user != null) {
                BigDecimal needPaymentAmount = categoryItemUtil.getTotalNeedPaymentAmountByUserId(noticeUserId);

                // 通知学生缴费
                String send_mp_content = "亲爱的" + user.getRealName() + "同学,目前您还有" + needPaymentAmount + "欠款,请及时支付!";
                newTipsUtil.addNewTips(TipsTypeEnum.TIPS_TYPE_ENUM_MESSAGE,
                        userId,
                        RangeTypeEnum.RANGE_TYPE_ENUM_PERSON,
                        Collections.singletonList(noticeUserId),
                        COPY_TYPE_ENUM_NONE,
                        null,
                        APP_TYPE_ENUM_PRESS_FOR_MONEY,
                        null,
                        null,
                        null,
                        null,
                        send_mp_content,
                        null,
                        null,
                        send_mp_content);
            }
        }

        return CommonResult.success();
    }

    @Override
    @Async
    public Future<ResponseResult> allStuStatusExport(HttpServletResponse response) {
        // 收集去重本计划范围内学生id
        List<String> stuIdList = this.list(new QueryWrapper<SmsWnsChargePlanStudentStatus>()
                        .select("DISTINCT(stu_id)"))
                .stream().map(SmsWnsChargePlanStudentStatus::getStuId).collect(Collectors.toList());

        // 查询学生信息
        List<SmsStudentInfo> studentInfoList = smsStudentInfoMapper.selectBatchIds(stuIdList);

        try {
            // 声明一个工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            // 获取sheet页数
            double sheetNum = Math.ceil((double) studentInfoList.size() / PER_SHEET_NUM);
            for (int k = 0; k < sheetNum; k++) {
                // 生成一个 sheet页,设置 sheet页 名称为 "学生欠缴费情况导出表"
                HSSFSheet sheet = workbook.createSheet("学生欠缴费情况导出表" + (k + 1));
                // 设置表格列宽度为 12
                sheet.setDefaultColumnWidth(12);

                // 设置单元格的显示样式
                HSSFCellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                // 设置单元格字体
                HSSFFont font = workbook.createFont();
                font.setFontHeightInPoints((short) 10); // 字号
                font.setBold(true); // 加粗
                cellStyle.setFont(font);

                // 创建第一行表头
                HSSFRow headRow = sheet.createRow(0);
                // 设置第一行数据
                int columnNum = 10; // 列数
                for (int i = 0; i < columnNum; i++) {
                    // 创建一个单元格
                    HSSFCell cell = headRow.createCell(i);
                    cell.setCellStyle(cellStyle);

                    String columnName = "";
                    switch (i) {
                        case 0:
                            columnName = "序号";
                            break;
                        case 1:
                            columnName = "年级名";
                            break;
                        case 2:
                            columnName = "专业名";
                            break;
                        case 3:
                            columnName = "班级名";
                            break;
                        case 4:
                            columnName = "学生姓名";
                            break;
                        case 5:
                            columnName = "学号";
                            break;
                        case 6:
                            columnName = "应缴合计";
                            break;
                        case 7:
                            columnName = "实收合计";
                            break;
                        case 8:
                            columnName = "欠缴合计";
                            break;
                        case 9:
                            columnName = "应退合计";
                            break;
                        default:
                            columnName = "";
                    }

                    // 创建一个内容对象
                    HSSFRichTextString text = new HSSFRichTextString(columnName);
                    // 将内容对象的文字内容写入到单元格中
                    cell.setCellValue(text);
                }

                // 设置内容
                int range = SmsSignUpCommonConstant.PER_SHEET_NUM;
                // 如果是最后一个 sheet 页, 修改 range 值
                if (k + 1 == sheetNum) {
                    range = studentInfoList.size() - SmsSignUpCommonConstant.PER_SHEET_NUM * k;
                }
                // 设置其他行数据
                for (int i = 0; i < range; i++) {
                    // 获取学生数据
                    SmsStudentInfo studentInfo = studentInfoList.get(SmsSignUpCommonConstant.PER_SHEET_NUM * k + i);

                    // 获取学生应缴费数据
                    SmsWnsChargePlanStudentStatusResp totalStatus = categoryItemUtil.getTotalStatusByStuId(studentInfo.getId());

                    // 设置数据
                    // 创建一行
                    HSSFRow row = sheet.createRow(i + 1);
                    // 序号
                    row.createCell(0).setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));
                    // 设置除序号外其他数据
                    for (int j = 1; j < columnNum; j++) {
                        String data;
                        switch (j) {
                            case 1: // 年级名
                                data = studentInfo.getStuGrade();
                                break;
                            case 2: // 专业名
                                data = studentInfo.getStuMajor();
                                break;
                            case 3: // 班级名
                                data = studentInfo.getStuClass();
                                break;
                            case 4: // 学生姓名
                                data = studentInfo.getStuName();
                                break;
                            case 5: // 学号
                                data = studentInfo.getStuNo();
                                break;
                            case 6: // 应缴合计
                                data = totalStatus.getTotalAmount().toString();
                                break;
                            case 7: // 实收合计
                                data = totalStatus.getPaymentAmount().toString();
                                break;
                            case 8: // 欠缴合计
                                data = totalStatus.getNeedPaymentAmount().toString();
                                break;
                            case 9: // 应退合计
                                data = totalStatus.getNeedPaymentAmount().negate().toString();
                                break;
                            default: // 其他
                                data = "";
                        }
                        row.createCell(j).setCellValue(new HSSFRichTextString(data == null ? "" : data));
                    }
                }
            }

            // 设置 response
            // response.reset();
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("学生欠缴费情况导出表.xlsx", "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode("学生欠缴费情况导出表.xlsx", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            // 刷新缓冲
            response.flushBuffer();
            // workbook将Excel写入到response的输出流中，供页面下载
            OutputStream os = response.getOutputStream();
            workbook.write(os);
            workbook.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }

        return new AsyncResult<>(CommonResult.success());
    }

    @Override
    public ResponseResult listByPlanId(String planId, String userId) {
        SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(planId);
        if (plan == null) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_NOT_EXIST);
        }

        List<SmsWnsChargePlanStudentStatus> studentStatusList = this.list(new LambdaQueryWrapper<SmsWnsChargePlanStudentStatus>()
                .eq(SmsWnsChargePlanStudentStatus::getPlanId, plan.getId())
                .eq(SmsWnsChargePlanStudentStatus::getUserId, userId));
        if (studentStatusList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_MISSING);
        }

        // 获取所有收费项
        List<String> detailIdList = JSON.parseArray(plan.getDetailIdList(), String.class);
        List<SmsWnsChargeCategoryDetail> detailList = smsWnsChargeCategoryDetailMapper.selectBatchIds(detailIdList);
        if (detailList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_CATEGORY_DETAIL_NOT_EXIST);
        }

        // 构造返回参数
        List<String> categoryIdList = detailList.stream().map(SmsWnsChargeCategoryDetail::getCategoryId).distinct().collect(Collectors.toList());
        List<SmsWnsChargeCategory> categoryList = smsWnsChargeCategoryMapper.selectList(new LambdaQueryWrapper<SmsWnsChargeCategory>()
                .in(SmsWnsChargeCategory::getId, categoryIdList)
                .orderByAsc(SmsWnsChargeCategory::getSort));
        List<SmsWnsChargeCategoryResp> collect = categoryList.stream().map(category -> {
            SmsWnsChargeCategoryResp categoryResp = new SmsWnsChargeCategoryResp();
            BeanUtils.copyProperties(category, categoryResp);

            List<SmsWnsChargeCategoryDetailResp> detailRespList = detailList.stream().filter(detail -> detail.getCategoryId().equals(category.getId()))
                    .map(detail -> {
                        SmsWnsChargeCategoryDetailResp detailResp = new SmsWnsChargeCategoryDetailResp();
                        BeanUtils.copyProperties(detail, detailResp);

                        List<SmsWnsChargePlanStudentStatus> filterStatusList = studentStatusList.stream().filter(status -> status.getCategoryDetailId().equals(detail.getId())).collect(Collectors.toList());
                        SmsWnsChargePlanStudentStatus status = filterStatusList.get(0);
                        SmsWnsChargePlanStudentStatusResp statusResp = new SmsWnsChargePlanStudentStatusResp();
                        BeanUtils.copyProperties(status, statusResp);

                        // 设置总金额以及优惠金额
                        SmsWnsChargeOrgItem chargeOrgItem = smsWnsChargeOrgItemMapper.selectById(status.getChargeItemId());
                        if (chargeOrgItem != null) {
                            statusResp.setChargeItem(chargeOrgItem);
                        }
                        detailResp.setStatusResp(statusResp);

                        return detailResp;
                    }).sorted(Comparator.comparingInt(SmsWnsChargeCategoryDetailResp::getSort)) // 根据sort排序
                    .collect(Collectors.toList());
            categoryResp.setDetailRespList(detailRespList);
            return categoryResp;
        }).collect(Collectors.toList());

        return CommonResult.success(collect);
    }

    @Override
    @Transactional
    public ResponseResult offlinePayment(SmsWnsChargePaymentRecordReq req, String userId) {
        List<PaymentCategoryDivideReq> divideReqList = req.getDivideReqList();
        if (divideReqList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        // 获取各收费项
        List<String> statusIdList = divideReqList.stream().map(PaymentCategoryDivideReq::getStudentStatusId).collect(Collectors.toList());
        List<SmsWnsChargePlanStudentStatus> statusList = smsWnsChargePlanStudentStatusMapper.selectBatchIds(statusIdList);
        if (statusList.size() != statusIdList.size()) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_MISSING);
        }

        // 校验是否属于同一个人
        List<String> userIdList = statusList.stream().map(SmsWnsChargePlanStudentStatus::getUserId).distinct().collect(Collectors.toList());
        if (userIdList.size() != 1) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_NOT_BELONG_SAME_PERSON);
        }

        // 校验是否属于同一个计划
        List<String> planIdList = statusList.stream().map(SmsWnsChargePlanStudentStatus::getPlanId).distinct().collect(Collectors.toList());
        if (planIdList.size() != 1) {
            return CommonResult.failed(CommonCodeEnum.WNS_PLAN_STUDENT_STATUS_NOT_BELONG_SAME_PLAN);
        }

        // 校验计划是否已暂停
        SmsWnsChargePlan plan = smsWnsChargePlanMapper.selectById(planIdList.get(0));
        if (plan.getIsPause()) {
            return CommonResult.failed(CommonCodeEnum.WNS_CHARGE_PLAN_PAUSE);
        }

        // 生成支付记录
        SmsWnsChargePaymentRecord paymentRecord = new SmsWnsChargePaymentRecord(); // 新生成的支付记录
        paymentRecord.setType(plan.getType());
        paymentRecord.setMode(CHARGE_PAYMENT_RECORD_MODE_OFFLINE); // 线下缴费
        paymentRecord.setEventId(plan.getId());
        paymentRecord.setUserId(userId);
        paymentRecord.setStuId(statusList.get(0).getStuId());
        smsWnsChargePaymentRecordMapper.insert(paymentRecord);

        // 计算总金额
        BigDecimal totalAmount = new BigDecimal(0); // 付款总金额
        Date nowDate = new Date();
        for (PaymentCategoryDivideReq divideReq : divideReqList) {
            // 获取对应的收费项
            SmsWnsChargePlanStudentStatus studentStatus = statusList.stream().filter(status -> status.getId().equals(divideReq.getStudentStatusId())).collect(Collectors.toList()).get(0);

            // 校验线下缴费金额
            if (divideReq.getOfflineAmount().compareTo(BigDecimal.ZERO) < 0 // 线下缴费金额小于等于0
                    || divideReq.getOfflineAmount().compareTo(studentStatus.getNeedPaymentAmount()) > 0) { // 线下缴费金额 > 欠费金额
                ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.WNS_PAYMENT_DIVIDE_AMOUNT_ILLEGAL));
            }

            // 校验之前是否选择本项,未选择则修改本项
            if (!studentStatus.getIsNecessary()) {
                studentStatus.setIsNecessary(true); // 选择此项
            }

            // 设置线下缴费金额
            if (studentStatus.getOfflineAmount() == null) {
                studentStatus.setOfflineAmount(divideReq.getOfflineAmount());
            } else {
                studentStatus.setOfflineAmount(studentStatus.getOfflineAmount().add(divideReq.getOfflineAmount()));
            }

            // 设置线下支付方式
            if (divideReq.getOfflineMethod() == null || !CHARGE_PLAN_STUDENT_STATUS_OFFLINE_METHOD_LIST.contains(divideReq.getOfflineMethod())) {
                studentStatus.setOfflineMethod(CHARGE_PLAN_STUDENT_STATUS_OFFLINE_METHOD_UNKNOWN);
            } else {
                studentStatus.setOfflineMethod(divideReq.getOfflineMethod());
            }

            // 设置线下操作人user_id
            if (isNullOrEmpty(studentStatus.getOfflineOperatorUserId())) {
                studentStatus.setOfflineOperatorUserId(JSON.toJSONString(Collections.singletonList(userId)));
            } else {
                List<String> operatorUserIdList = JSON.parseArray(studentStatus.getOfflineOperatorUserId(), String.class);
                operatorUserIdList.add(userId);
                studentStatus.setOfflineOperatorUserId(JSON.toJSONString(operatorUserIdList.stream().distinct().collect(Collectors.toList())));
            }

            // 设置欠费金额(欠费金额 = 当前欠费金额 - 线下缴费金额)
            BigDecimal needPaymentAmount = studentStatus.getNeedPaymentAmount().subtract(divideReq.getOfflineAmount());
            studentStatus.setNeedPaymentAmount(needPaymentAmount);
            if (needPaymentAmount.compareTo(BigDecimal.ZERO) <= 0) {
                studentStatus.setIsPaid(true); // 设置当前缴费项为已交齐
            }
            smsWnsChargePlanStudentStatusMapper.updateById(studentStatus);

            // 生成金额分配数据
            SmsWnsChargePaymentCategoryDivide categoryDivide = new SmsWnsChargePaymentCategoryDivide();
            categoryDivide.setPaymentRecordId(paymentRecord.getId());
            categoryDivide.setPlanId(plan.getId());
            categoryDivide.setUserId(userId);
            categoryDivide.setCategoryId(studentStatus.getCategoryId());
            categoryDivide.setCategoryDetailId(studentStatus.getCategoryDetailId());
            categoryDivide.setChargeItemId(studentStatus.getChargeItemId());
            categoryDivide.setAmount(divideReq.getOfflineAmount());
            categoryDivide.setPaymentAmount(divideReq.getOfflineAmount());
            categoryDivide.setNeedPayAmount(needPaymentAmount);
            categoryDivide.setPaymentTime(nowDate);
            smsWnsChargePaymentCategoryDivideMapper.insert(categoryDivide);

            totalAmount = totalAmount.add(divideReq.getOfflineAmount());
        }

        // 修改支付记录支付金额
        paymentRecord.setPaymentAmount(totalAmount);
        paymentRecord.setPaymentTime(nowDate);
        smsWnsChargePaymentRecordMapper.updateById(paymentRecord);

        // 新生缴费设置新生该流程为进行中
        List<SmsWnsRegistrationProcessDetail> detailList = smsWnsRegistrationProcessDetailMapper.selectList(new LambdaQueryWrapper<SmsWnsRegistrationProcessDetail>()
                .eq(SmsWnsRegistrationProcessDetail::getUserId, userId)
                .eq(SmsWnsRegistrationProcessDetail::getSort, REGISTRATION_PROCESS_DETAIL_SORT_TWO) // 财务处报道
                .eq(SmsWnsRegistrationProcessDetail::getStatus, REGISTRATION_PROCESS_DETAIL_STATUS_NOT_START) // 未开始
                .orderByDesc(SmsWnsRegistrationProcessDetail::getCreateTime));
        if (!detailList.isEmpty()) {
            SmsWnsRegistrationProcessDetail detail = detailList.get(0);
            detail.setStatus(REGISTRATION_PROCESS_DETAIL_STATUS_START); // 设置为进行中
            smsWnsRegistrationProcessDetailMapper.updateById(detail);
        }

        return CommonResult.success();
    }

    /**
     * 创建错误记录
     *
     * @param id     收费范围内元素id
     * @param name   元素名
     * @param taskId 任务id
     * @param reason 错误原因
     * @param row    错误行数
     */
    private void createImportTaskDetail(String id, String name, String taskId, String reason, String row) {
        SmsImportTaskDetail smsImportTaskDetail = new SmsImportTaskDetail();
        smsImportTaskDetail.setTaskId(taskId); // 任务id
        smsImportTaskDetail.setFailReason(reason); // 错误原因
        smsImportTaskDetail.setFailName(name); // 元素名
        smsImportTaskDetail.setFailNo(id); // 收费范围内元素id
        smsImportTaskDetail.setFailRow(row); // 错误行数
        smsImportTaskDetailMapper.insert(smsImportTaskDetail);
    }

    private void setTableHead(HSSFRow headRow, HSSFCellStyle headerStyle, Integer cell_num, String headContent) {
        HSSFCell cell = headRow.createCell(cell_num);
        // 创建一个内容对象
        HSSFRichTextString text = new HSSFRichTextString(headContent);

        // 将内容对象的文字内容写入到单元格中
        cell.setCellValue(text);
        cell.setCellStyle(headerStyle);
    }

    /**
     * @param exportReq 导出的条件
     * @param type      1:明细缴费记录 2:总缴费记录
     * @return 导出的数据
     */
    private List<SmsStudentPaymentRecordsExportResp> getStudentPaymentRecords(SmsWnsChargePlanStudentStatusExportReq exportReq, Integer type) {
        List<SmsStudentPaymentRecordsExportResp> studentPaymentRecordsList = new ArrayList<>();
        QueryWrapper<SmsWnsChargePlanStudentStatus> studentStatusLambdaQueryWrapper = new QueryWrapper<>();
        QueryWrapper<SmsWnsChargePlanStudentStatus> studentStatusQueryWrapper = constructionConditions(studentStatusLambdaQueryWrapper,
                exportReq.getPlanId(), null, null, exportReq.getStuName(), 1, exportReq.getOrgId(), exportReq.getIsPaid());

        List<Map<String, Object>> mapList = smsWnsChargePlanStudentStatusMapper.selectMaps(studentStatusQueryWrapper
                .select("user_id as userId")
                .lambda()
                .groupBy(SmsWnsChargePlanStudentStatus::getUserId));

        if (!mapList.isEmpty()) {
            //userId集合
            List<SmsStudentPaymentDetailResp> smsStudentPaymentDetailList = JSONObject.parseArray(JSON.toJSONString(mapList), SmsStudentPaymentDetailResp.class);
            studentPaymentRecordsList = smsStudentPaymentDetailList.stream().filter(i -> {
                SmsUserStudentRelation smsUserStudentRelation = smsUserStudentRelationMapper.selectOne(new LambdaQueryWrapper<SmsUserStudentRelation>()
                        .eq(SmsUserStudentRelation::getUserId, i.getUserId()));
                if (isNullOrEmpty(smsUserStudentRelation)) {
                    return false;
                }
                return true;
            }).map(smsStudentPaymentDetail -> {
                QueryWrapper<SmsWnsChargePlanStudentStatus> statusLambdaQueryWrapper = new QueryWrapper<>();
                QueryWrapper<SmsWnsChargePlanStudentStatus> studentWrapper = constructionConditions(statusLambdaQueryWrapper,
                        exportReq.getPlanId(), null, null, exportReq.getStuName(), 2, exportReq.getOrgId(), exportReq.getIsPaid());
                List<SmsWnsChargePlanStudentStatus> studentStatusList = smsWnsChargePlanStudentStatusMapper.selectList(studentWrapper
                        .lambda()
                        .eq(SmsWnsChargePlanStudentStatus::getUserId, smsStudentPaymentDetail.getUserId()));

                //学生信息和缴费信息
                SmsStudentPaymentRecordsExportResp smsStudentPaymentRecordsResp = new SmsStudentPaymentRecordsExportResp();
                List<SmsStudentPaymentDetailResp> studentPaymentDetailList = new ArrayList<>();
                BigDecimal totalAmountSum = new BigDecimal(0);
                BigDecimal needPaymentAmountSum = new BigDecimal(0);
                BigDecimal paymentAmountSum = new BigDecimal(0);
                //辅导员
                List<SmsUser> assistantInfo = assistantUtil.getAssistantByUserId(smsStudentPaymentDetail.getUserId());
                if (assistantInfo != null && assistantInfo.size() > 0) {
                    smsStudentPaymentRecordsResp.setAssistantInfo(assistantInfo.get(0));
                }
                SmsUserStudentRelation smsUserStudentRelation = smsUserStudentRelationMapper.selectOne(new LambdaQueryWrapper<SmsUserStudentRelation>()
                        .eq(SmsUserStudentRelation::getUserId, smsStudentPaymentDetail.getUserId()));

                if (!isNullOrEmpty(smsUserStudentRelation)) {
                    String stuId = smsUserStudentRelation.getStuId();
                    SmsStudentInfo smsStudentInfo = smsStudentInfoMapper.selectById(stuId);
                    if (!isNullOrEmpty(smsStudentInfo)) {
                        BeanUtils.copyProperties(smsStudentInfo, smsStudentPaymentRecordsResp);
                        //缴费信息(应缴,实缴,欠缴)
                        for (SmsWnsChargePlanStudentStatus smsWnsChargePlanStudentStatus : studentStatusList) {
                            //应缴费总计
                            totalAmountSum = totalAmountSum.add(smsWnsChargePlanStudentStatus.getTotalAmount());
                            //欠费总计
                            needPaymentAmountSum = needPaymentAmountSum.add(smsWnsChargePlanStudentStatus.getNeedPaymentAmount());
                            //缴费明细
                            if (type == 1) {
                                SmsStudentPaymentDetailResp studentPaymentDetailResp = new SmsStudentPaymentDetailResp();
                                SmsWnsChargeCategoryDetail smsWnsChargeCategoryDetail = smsWnsChargeCategoryDetailMapper.selectAllById(smsWnsChargePlanStudentStatus.getCategoryDetailId());
                                if (!isNullOrEmpty(smsWnsChargeCategoryDetail)) {
                                    String categoryDetailName = smsWnsChargeCategoryDetail.getName();
                                    studentPaymentDetailResp.setCategoryDetailName(categoryDetailName);
                                    BeanUtils.copyProperties(smsWnsChargePlanStudentStatus, studentPaymentDetailResp);
                                    //实缴总计
                                    BigDecimal paymentAmount = new BigDecimal(0);
                                    if (!isNullOrEmpty(smsWnsChargePlanStudentStatus.getPaymentAmount())) {
                                        paymentAmount = smsWnsChargePlanStudentStatus.getPaymentAmount();
                                    }
                                    studentPaymentDetailResp.setPaymentAmount(paymentAmount);
                                    paymentAmountSum = paymentAmountSum.add(paymentAmount);
                                }
                                studentPaymentDetailList.add(studentPaymentDetailResp);
                                //总的缴费记录
                            } else if (type == 2) {
                                //实缴总计
                                BigDecimal paymentAmount = new BigDecimal(0);
                                if (!isNullOrEmpty(smsWnsChargePlanStudentStatus.getPaymentAmount())) {
                                    paymentAmount = smsWnsChargePlanStudentStatus.getPaymentAmount();
                                }
                                paymentAmountSum = paymentAmountSum.add(paymentAmount);
                            }
                        }
                        smsStudentPaymentRecordsResp.setStudentPaymentDetailList(studentPaymentDetailList);
                        smsStudentPaymentRecordsResp.setTotalAmountSum(totalAmountSum);
                        smsStudentPaymentRecordsResp.setPaymentAmountSum(paymentAmountSum);
                        smsStudentPaymentRecordsResp.setNeedPaymentAmountSum(needPaymentAmountSum);
                    }
                }
                return smsStudentPaymentRecordsResp;

            }).collect(Collectors.toList());
        }
        return studentPaymentRecordsList;
    }

    /**
     * @param studentStatusQueryWrapper 构造条件
     * @param planId                    计划id
     * @param categoryId                收费项id
     * @param categoryDetailId          小收费项id
     * @param stuName                   学生姓名
     * @param type                      type 为1 构造user_id分组的条件,其他type不用
     * @param orgId                     组织id
     * @param isPaid                    是否缴齐
     * @return 根据条件进行userId分组
     */
    private QueryWrapper<SmsWnsChargePlanStudentStatus> constructionConditions(QueryWrapper<SmsWnsChargePlanStudentStatus> studentStatusQueryWrapper, String planId, String categoryId, String categoryDetailId,
                                                                               String stuName, Integer type, String orgId, Boolean isPaid) {
        //计划
        if (!isNullOrEmpty(planId)) {
            studentStatusQueryWrapper.lambda().eq(SmsWnsChargePlanStudentStatus::getPlanId, planId);
        }
        //大分类
        if (!isNullOrEmpty(categoryId)) {
            studentStatusQueryWrapper.lambda().eq(SmsWnsChargePlanStudentStatus::getCategoryId, categoryId);
        }
        //小分类
        if (!isNullOrEmpty(categoryDetailId)) {
            studentStatusQueryWrapper.lambda().eq(SmsWnsChargePlanStudentStatus::getCategoryDetailId, categoryDetailId);
        }
        //是否缴齐(type 为1 构造user_id分组的条件,其他type不用)
        if (type == 1 && !isNullOrEmpty(isPaid)) {
//            studentStatusQueryWrapper.lambda().eq(SmsWnsChargePlanStudentStatus::getIsPaid, isPaid);
            if (isPaid) {
                studentStatusQueryWrapper.having("COUNT(*) = SUM(CASE WHEN is_paid = " + true + " THEN 1 ELSE 0 END)");
            } else {
                studentStatusQueryWrapper.having("COUNT(*) > SUM(CASE WHEN is_paid = " + true + " THEN 1 ELSE 0 END)");
            }

        }

        //学生姓名
        if (!isNullOrEmpty(stuName)) {
            List<String> stuIdList = smsStudentInfoMapper.selectList(new LambdaQueryWrapper<SmsStudentInfo>()
                            .like(SmsStudentInfo::getStuName, stuName).or()
                            .like(SmsStudentInfo::getStuNo, stuName).or()
                            .like(SmsStudentInfo::getStuCollege, stuName).or()
                            .like(SmsStudentInfo::getStuMajor, stuName).or()
                            .like(SmsStudentInfo::getStuClass, stuName).or()
                            .like(SmsStudentInfo::getStuIdCard, stuName))
                    .stream().map(SmsStudentInfo::getId).collect(Collectors.toList());
            if (!stuIdList.isEmpty()) {
                studentStatusQueryWrapper.lambda().in(SmsWnsChargePlanStudentStatus::getStuId, stuIdList);
            } else {
                studentStatusQueryWrapper.lambda().in(SmsWnsChargePlanStudentStatus::getStuId, "");
            }
        }
        //组织
        if (!isNullOrEmpty(orgId)) {
            List<String> userIdList = smsOrgUserRelationMapper.selectList(new LambdaQueryWrapper<SmsOrgUserRelation>()
                            .eq(SmsOrgUserRelation::getOrgId, orgId))
                    .stream().map(SmsOrgUserRelation::getUserId).collect(Collectors.toList());
            if (!userIdList.isEmpty()) {
                studentStatusQueryWrapper.lambda().in(SmsWnsChargePlanStudentStatus::getUserId, userIdList);
            } else {
                studentStatusQueryWrapper.lambda().in(SmsWnsChargePlanStudentStatus::getUserId, "");
            }
        }
        return studentStatusQueryWrapper;
    }


}
